package com.qcby.llmtchstd.utils;

import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.qcby.llmtchstd.entity.ChoiceQuestion;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.lang.reflect.Field;
import java.io.*;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

public class OfficeUtils {
    /**
     * 插入新段落
     *
     * @param paragraphs 段落集合
     * @param document   文档
     * @param index      插入位置
     * @return 新段落
     */
    public static XWPFParagraph insertNewParagraph(List<XWPFParagraph> paragraphs, XWPFDocument document, Integer index) {
        if (paragraphs.size() == index + 1) {
            return document.createParagraph();
        } else {
            return document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());
        }
    }

    /**
     * 设置表格宽度去除边框
     *
     * @param table 表格
     * @param width 宽度值
     */
    public static void setTableWidthToRemoveBorder(XWPFTable table, Integer width) {
        // 去除表格边框
        CTTblPr tblPr2 = table.getCTTbl().getTblPr();
        CTTblBorders borders2 = tblPr2.addNewTblBorders();
        borders2.addNewBottom().setVal(STBorder.NONE);
        borders2.addNewTop().setVal(STBorder.NONE);
        borders2.addNewLeft().setVal(STBorder.NONE);
        borders2.addNewRight().setVal(STBorder.NONE);
        borders2.addNewInsideH().setVal(STBorder.NONE);
        borders2.addNewInsideV().setVal(STBorder.NONE);

        // 设置表格整体样式
        tblPr2.addNewTblW().setW(BigInteger.valueOf(width)); // 设置表格宽度
    }

    /**
     * 设置表格单元格宽度及文本居中
     *
     * @param cell  单元格
     * @param width 宽度占比
     */
    public static void setTheLandscapeHeader(XWPFTableCell cell, double width) {
        setsTheCellWidth(cell, width);
        // 获取单元格属性对象
        CTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();
        // 设置垂直对齐方式为居中
        CTVerticalJc vJc = tcPr.isSetVAlign() ? tcPr.getVAlign() : tcPr.addNewVAlign();
        vJc.setVal(STVerticalJc.CENTER);

    }

    /**
     * 设置表格单元格宽度样式靠左
     *
     * @param cell  单元格
     * @param width 宽度占比
     */
    public static void setsTheCellWidthLeft(XWPFTableCell cell, double width) {
        // 假设A4纸宽约为210mm，1mm=360EMU，则A4宽约为7920EMU
        int emuFor30Percent = (int) (7920 * width);
        CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
        // 设置宽度为2000EMU，你可以根据需要调整这个值
        ctTblWidth.setW(BigInteger.valueOf(emuFor30Percent));
        // 设置宽度类型为字符单位（也可以是其他单位，如百分比等）
        ctTblWidth.setType(STTblWidth.PCT);
        // 设置垂直居中
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        for (XWPFParagraph para : cell.getParagraphs()) {
            //居中
            para.setAlignment(ParagraphAlignment.LEFT);
        }
    }

    /**
     * 设置表格单元格宽度
     *
     * @param cell  单元格
     * @param width 宽度占比
     */
    public static void setsTheCellWidth(XWPFTableCell cell, double width) {
        // 假设A4纸宽约为210mm，1mm=360EMU，则A4宽约为7920EMU
        int emuFor30Percent = (int) (7920 * width);
        CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
        // 设置宽度为2000EMU，你可以根据需要调整这个值
        ctTblWidth.setW(BigInteger.valueOf(emuFor30Percent));
        // 设置宽度类型为字符单位（也可以是其他单位，如百分比等）
        ctTblWidth.setType(STTblWidth.PCT);
        // 设置垂直居中
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        for (XWPFParagraph para : cell.getParagraphs()) {
            //居中
            para.setAlignment(ParagraphAlignment.CENTER);
        }
    }

    /**
     * 设置表格行的高度
     *
     * @param row      行
     * @param heightCm 高度占比
     */
    public static void setRowHeight(XWPFTableRow row, double heightCm) {
        int emuForHeight = (int) (360 * heightCm);
        CTTrPr trPr = row.getCtRow().addNewTrPr();
        CTHeight ht = trPr.addNewTrHeight();
        ht.setVal(BigInteger.valueOf(emuForHeight));
    }

    /**
     * 创建表格行
     *
     * @param table 表格
     * @param index 行索引
     * @return
     */
    public static XWPFTableRow createRow(XWPFTable table, int index) {
        return Objects.isNull(table.getRow(index)) ? table.createRow() : table.getRow(index);
    }

    /**
     * 创建单元格
     *
     * @param row   行
     * @param index 列索引
     * @return
     */
    public static XWPFTableCell createCell(XWPFTableRow row, int index) {
        return Objects.isNull(row.getCell(index)) ? row.createCell() : row.getCell(index);
    }

    /**
     * 获取文本在文档中的索引
     *
     * @param doc  文档
     * @param text 文本标识
     * @return
     */
    public static int findParagraphIndexByText(XWPFDocument doc, String text) {
        // 获取所有段落
        List<XWPFParagraph> paragraphs = doc.getParagraphs();
        // 查找目标段落
        int targetParagraphIndex = -1;
        for (int i = 0; i < paragraphs.size(); i++) {
            if (paragraphs.get(i).getText().contains(text)) {
                targetParagraphIndex = i;
                break;
            }
        }
        return targetParagraphIndex;
    }

    /**
     * 对象转Map（支持驼峰转下划线，同时生成两种占位符的key：${}和{{}}）
     * 例如：DTO字段choiceCount → map中包含"${choiceCount}"和"{{choice_count}}"
     */
    public static Map<String, Object> objectToMap(Object obj) {
        Map<String, Object> map = new HashMap<>();
        if (obj == null) {
            return map;
        }
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(obj);
                String camelCaseName = field.getName();
                String underscoreName = camelToUnderscore(camelCaseName);

                // 同时支持两种占位符格式：${驼峰} 和 {{下划线}}
                map.put("${" + camelCaseName + "}", value);    // 匹配 ${paperName}
                map.put("{{" + underscoreName + "}}", value);  // 匹配 {{choice_count}}
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * 驼峰命名转下划线命名（关键：让DTO字段适配模板的下划线占位符）
     */
    private static String camelToUnderscore(String camelCase) {
        if (camelCase == null || camelCase.isEmpty()) {
            return camelCase;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(Character.toLowerCase(camelCase.charAt(0)));
        for (int i = 1; i < camelCase.length(); i++) {
            char c = camelCase.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append("_").append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 段落文本填充（同时替换 ${} 和 {{}} 两种占位符）
     * @param document      文档
     * @param dataMap       数据Map（key: ${驼峰} 或 {{下划线}}, value: 要替换的内容）
     */
    public static void paragraphTextFilling(XWPFDocument document, Map<String, Object> dataMap) {
        // 将Map转成 {占位符字符串: 替换后的值} 的格式，方便遍历
        Map<String, String> replaceMap = dataMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue() != null ? entry.getValue().toString() : ""
                ));

        // 遍历所有段落
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            String originalText = paragraph.getText();
            if (originalText == null || originalText.isEmpty()) {
                continue;
            }

            // 替换所有占位符
            String replacedText = originalText;
            for (Map.Entry<String, String> entry : replaceMap.entrySet()) {
                String placeholder = entry.getKey();
                String value = entry.getValue();
                replacedText = replacedText.replace(placeholder, value);
            }

            // 如果文本有变化，更新段落
            if (!originalText.equals(replacedText)) {
                // 清空原有内容，重新设置（避免样式丢失）
                List<XWPFRun> runs = paragraph.getRuns();
                if (!runs.isEmpty()) {
                    runs.get(0).setText(replacedText, 0);
                    // 删除多余的run（如果有）
                    for (int i = runs.size() - 1; i > 0; i--) {
                        paragraph.removeRun(i);
                    }
                } else {
                    paragraph.createRun().setText(replacedText);
                }
            }
        }
    }

    /**
     * 解析选择题JSON并格式化为带换行的文本
     * @param jsonStr 数据库中的选择题JSON字符串
     * @return 格式化后的文本（题目占一行，选项带空格）
     */
    public static String formatChoiceQuestions(String jsonStr) {
        if (jsonStr == null || jsonStr.trim().isEmpty()) {
            return "无选择题";
        }

        // 1. 解析JSON为选择题列表
        List<ChoiceQuestion> questions = JSON.parseObject(
                jsonStr,
                new TypeReference<List<ChoiceQuestion>>() {}
        );

        // 2. 拼接成带换行和空格的文本
        StringBuilder sb = new StringBuilder();
        for (ChoiceQuestion question : questions) {
            // 题目行（示例：1、下列属于Java基本数据类型的是？（2分））
            sb.append(question.getId())
                    .append("、")
                    .append(question.getContent())
                    .append("（")
                    .append(question.getScore())
                    .append("分）\n");

            // 选项行（示例：   A.String   B.int   C.ArrayList   D.HashMap）
            List<String> options = question.getOptions();
            for (int i = 0; i < options.size(); i++) {
                // 选项前缀：A、B、C、D...
                char optionPrefix = (char) ('A' + i);
                sb.append("   ") // 选项前加3个空格缩进
                        .append(optionPrefix)
                        .append(".")
                        .append(options.get(i));
            }
            sb.append("\n"); // 每个题目结束后换行
        }

        return sb.toString();
    }


    /**
     * 处理Word响应（优化中文文件名编码）
     * @param downloadName 下载文件名（无需后缀，方法内自动加.docx）
     * @param inputStream  文件输入流
     * @param response     响应
     */
    public static void processingWordResponses(String downloadName,
                                               InputStream inputStream,
                                               HttpServletResponse response) {
        try {
            String encodedName = URLEncoder.encode(downloadName, StandardCharsets.UTF_8.toString())
                    .replace("+", "%20");

            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + encodedName + ".docx\";" +
                            " filename*=UTF-8''" + encodedName + ".docx");

            OutputStream outputStream = response.getOutputStream();
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * word转InputStream
     *
     * @param document
     * @return
     */
    public static InputStream writeDocumentToInputStream(XWPFDocument document) {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            document.write(byteArrayOutputStream);
            byteArrayOutputStream.close();
            return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}
